package Analizadores.Utilidades;

import java.util.ArrayList;
import java.util.HashMap;

import Analizadores.AnalizadorLexico.CategoriaLexica;
import Analizadores.AnalizadorSintactico.Tipos;
import Analizadores.Estructuras.PilaTipos;
import Analizadores.Estructuras.TablaAuxiliar;
import Analizadores.Estructuras.TablaFunciones;
import Analizadores.Estructuras.TablaSimbolos;
import Analizadores.Estructuras.TokenList;
import Analizadores.Estructuras.Tipos.Clase;
import Analizadores.Estructuras.Tipos.Direcciones;
import Analizadores.Estructuras.Tipos.RegAux;
import Analizadores.Estructuras.Tipos.RegFun;
import Analizadores.Estructuras.Tipos.RegistroTS;
import Analizadores.Estructuras.Tipos.Token;

//analizador sintactico y semantico
public class ProcFun {

	private TokenList lista;//lista de token generados por el lexico
	private TablaSimbolos ts;//tabla de simbolos para las variables
	private PilaTipos pila;//pila de tipos para el control de tipado
	private Comprobaciones tablas;//Control de operaciones y tipos aceptados
	private Errores error;//clase para el mostrar el error por pantalla
	private TablaAuxiliar t;
	private TablaAuxiliar tss;
	private TablaFunciones tf;
	ArrayList<Tablas> tabla;
	
	private String decTipo;//tipo de la variable que se esta declarando
	private Token tokenActual;//token actual que se esta procesando
	private int numInstruccion;//numero de instruccion en la que se encuentra el analizador
	private int numDir;// numero de direcciones ocupadas
	private int pos;
	private Tipos tipoMem;
	private int dirMem;
	private String ant;
	private int nivel;
	private Tipos tReturn;
	private int numPara;
	private int numDirPar;
	

	private GenCodigo genCodigo;//clase para la generacion de codigo
	

	
	//constructora donde inicializamos todas las variables
	public ProcFun(TokenList lista,Errores er,GenCodigo gen,ArrayList<Tablas> todas,int n,int numIns,PilaTipos p,Tipos tRet,TablaSimbolos tsPar,TablaAuxiliar tPar,int numDirP){
		this.lista = lista;
		numInstruccion=numIns;
		numDir = numDirP;
		//HashMap<String, RegistroTS> tsim = new HashMap<String,RegistroTS>();
		ts = tsPar;
		pila = p;
		numPara = numDir;
		tablas = new Comprobaciones();
		error = er;
		genCodigo = gen;

		t = new TablaAuxiliar();
		tss = tPar;
		tf = new TablaFunciones();
		nivel = n;
		tReturn = tRet;
		nivel++;
		tabla = todas;
		Tablas tttt = new Tablas(t,tss,tf,ts);
		tabla.add(tttt);
	}
	//getter del codigo
	public String getCodigo(){
		return genCodigo.getCodigo();
	}
	public ArrayList<String> getCod(){
		return genCodigo.getCod();
	}
	//asigna a token actual el token siguiente en la lista
	private void sigToken(){
		tokenActual = lista.getToken();
	}
	//funcion que procesa todos los token en orden de primero declaracion de variables y despues de acciones
	public void procesar(){
		genCodigo.generaFun(nivel);
		
		SDec();//declaracion de variables
		//generaCodigoTipos();	
		genCodigo.genAmpersan();
		genCodigo.desapilaDirBloque(0, nivel);//escribimos un ampersan para que la maquina diferencia entre declaracion de variables y de acciones
		guardaParametros();
		SAc();
		// declaracion de acciones	
		genCodigo.generaVolver(nivel);
		genCodigo.generaEnd(nivel);
		
	}
	public void guardaParametros(){
		for(int i = numPara-1;i>=2;i--){
			genCodigo.desapilaDirBloque(i, nivel);
		}
	}
	
	private void convDec(ArrayList<Object> al,String id){
		String s = (String)al.get(0);
		int ind = s.indexOf(";");
		String aux = s.substring(2, ind);
		if(aux.equals("entero")){
			RegAux reg = new RegAux(t.getNum(),Tipos.entero,null);
			tss.anade(id, reg);
		}
		else if(aux.equals("real")){
			RegAux reg = new RegAux(t.getNum(),Tipos.real,null);
			tss.anade(id, reg);
		}
		else if(aux.equals("ref")){
			ind = s.indexOf(":",ind)+1;
			aux = s.substring(ind, s.length());
			int n = nivelIdTipo(aux);
			TablaAuxiliar tTip = tabla.get(n).getTablaTipos();
			RegAux reg = tTip.getReg(aux);
			tss.anade(id, reg);
		}
	}
	
	@SuppressWarnings("unchecked")
	private void conv(ArrayList<Object> al,String id,boolean b){
		String s = (String)al.get(0);
		int ind = s.indexOf(";");
		String aux = s.substring(2, ind);
		if(aux.equals("entero")){
			RegAux reg = new RegAux(t.getNum(),Tipos.entero,null,b);
			t.anade(id, reg);
		}
		else if(aux.equals("real")){
			RegAux reg = new RegAux(t.getNum(),Tipos.real,null,b);
			t.anade(id, reg);
		}
		else if(aux.equals("ref")){
			ind = s.indexOf(":",ind)+1;
			aux = s.substring(ind, s.length());
			int n = nivelIdTipo(aux);
			TablaAuxiliar tTip = tabla.get(n).getTablaTipos();
			RegAux reg = tTip.getReg(aux);
			reg.setPunt(b);
			t.anade(id, reg);
		}
		else if(aux.equals("rec")){

			ArrayList<Object> a = (ArrayList<Object>)al.get(1);
			TablaAuxiliar ta = lisCampos(a);
			RegAux reg = new RegAux(t.getNum(),Tipos.registro,ta);
			t.anade(id,reg);
		}
		else if(aux.equals("array")){
			ind = s.indexOf(";",ind+1);
			aux = s.substring(14,ind);
			int numEl = Integer.parseInt(aux);
			TablaAuxiliar ta = arrayTipo(numEl,(ArrayList<Object>)al.get(1));
			RegAux reg = new RegAux(t.getNum(),Tipos.array,ta);
			t.anade(id,reg);
		}
		else if(aux.equals("puntero")){
			TablaAuxiliar ta = punter((ArrayList<Object>)al.get(1));
			RegAux reg = new RegAux(t.getNum(),Tipos.puntero,ta);
			t.anade(id,reg);
		}	
	}
	private TablaAuxiliar punter(ArrayList<Object> al){
		TablaAuxiliar ta = new TablaAuxiliar();
		ta = convCampos(ta,al,"^");
		return ta;
	}
	private TablaAuxiliar arrayTipo(int numEl,ArrayList<Object> tip){
		TablaAuxiliar ta = new TablaAuxiliar();
		
		for(int i = 0;i<numEl;i++){
			ta = convCampos(ta,tip,Integer.toString(i));
		}
		ta.getReg("0").setNumElems(numEl);
		return ta;
	}
	
	@SuppressWarnings("unchecked")
	private TablaAuxiliar lisCampos(ArrayList<Object> al){
		TablaAuxiliar ta = new TablaAuxiliar();
		for(int i = 0;i<al.size();i++){
			ta = campos(ta,(ArrayList<Object>)al.get(i));
			
		}
		return ta;
	}

	@SuppressWarnings("unchecked")
	private TablaAuxiliar campos(TablaAuxiliar ta,ArrayList<Object> al){
		String s = (String)al.get(0);
		int ind = s.indexOf(";");
		String id = s.substring(3, ind);
		ta = convCampos(ta,(ArrayList<Object>)al.get(1),id);
		return ta;
	}
	private TablaAuxiliar convCampos(TablaAuxiliar ta,ArrayList<Object> al,String id){
		String s = (String)al.get(0);
		int ind = s.indexOf(";");
		String aux = s.substring(2, ind);
		if(aux.equals("entero")){
			RegAux reg = new RegAux(ta.getNum(),Tipos.entero,null);
			if(!ta.anade(id, reg))
				error.escribeSemantico("Nombre de variable repetida en registro, id repetido -> "+id);
			return ta;
		}
		else if(aux.equals("real")){
			RegAux reg = new RegAux(ta.getNum(),Tipos.real,null);
			if(!ta.anade(id, reg))
				error.escribeSemantico("Nombre de variable repetida en registro, id repetido -> "+id);
			return ta;
		}
		else if(aux.equals("ref")){
			ind = s.indexOf(":",ind)+1;
			aux = s.substring(ind, s.length());
			RegAux reg = t.getReg(aux);
			reg.setNumDir(ta.getNum());
			if(!ta.anade(id, reg))
				error.escribeSemantico("Nombre de variable repetida en registro, id repetido -> "+id);
			return ta;
		}
		else if(aux.equals("rec")){
			@SuppressWarnings("unchecked")
			ArrayList<Object> a = (ArrayList<Object>)al.get(1);
			TablaAuxiliar tac = lisCampos(a);
			RegAux reg = new RegAux(ta.getNum(),Tipos.registro,tac);
			if(!ta.anade(id, reg))
				error.escribeSemantico("Nombre de variable repetida en registro, id repetido -> "+id);
			ta.setNum(tac.getNum()+ta.getNum());
			return ta;
		}
		return null;
	}
	/******************************************************************************
	 * **************DECLARACION DE VARIABLES*************************************
	*******************************************************************************/
	private void SDec(){
		//SDec->Dec SDec
		//SDec -> *
		sigToken();//pedimos el siguiente token
		if (matchTipo()){//miramos si es de algun tipo de los posibles
			dec();//llamamos a la funcion para procesar la declaracion de variable
			SDec();	//volvemos para ver si hay mas declaraciones		
		}
	}
	//metodo para procesar la declaracion de la variable
	private void dec(){
		//Dec-> Tipo variable ;
		if(esTipo()){
			tipoConstruido();			
		}
		else if(esFuncion()){
			decFun();
		}
		else if(esDeclaracion()){		
			variable();
		}
	}
	private void decFun(){
		int etq = genCodigo.getEtq();
		sigToken();
		numDirPar = 2;
		if(tokenActual.cat != CategoriaLexica.CLID)
			error.escribe("Se espera un id para la funcion", numInstruccion, tokenActual);
		if(existeEnTS(tokenActual.lex))
			error.escribeSemantico("Nombre de variable repetido", numInstruccion, tokenActual);
		Token tokenId = tokenActual;
		if(existeEnTS(tokenActual.lex)||existeTipoEnTS(tokenActual.lex)){
			error.escribe("Ya se ha declarado un variable con el mismo nombre", numInstruccion, tokenActual);
		}
		sigToken();//empezamos con los parametros
		if(tokenActual.cat != CategoriaLexica.CLPA)
			error.escribe("Se espera un parentesis abierto para la declaracion de parametros", numInstruccion, tokenActual);
		TablaAuxiliar tPar = new TablaAuxiliar();
		TablaSimbolos tsPar = new TablaSimbolos(new HashMap<String,RegistroTS>());
		ArrayList<String> al = new ArrayList<String>();
		decParam(tPar,tsPar,al);
		
		if(tokenActual.cat != CategoriaLexica.CLPC)//comprobamos que se cierra el parentesis, sino, error
			error.escribe("No cierre de parentesis",numInstruccion,tokenActual);
		Tipos tip = Tipos.entero;
		if(lista.mirarToken(1).cat == CategoriaLexica.CLRETURNS){
			sigToken();
			sigToken();
			tip = esTipoRet();
			if(tip == Tipos.error)
				error.escribe("Token no esperado", numInstruccion, tokenActual);
			
			sigToken();
			//ai ke mirar ke es int, real o algun tipo que sea int
		}
		
		RegFun reg = new RegFun(al,tip,etq+1);
		tf.anadeId(tokenId.lex, reg);
		RegistroTS rega = new RegistroTS();
		ts.anadeId(tokenId.lex, rega);
		ProcFun pf = new ProcFun(lista,error,genCodigo,tabla,nivel,numInstruccion,pila,tip,tsPar,tPar,numDirPar);//ai ke poner ke pare en el end
		pf.procesar();
		
		if(lista.mirarToken(0).cat != CategoriaLexica.CLEND)
			error.escribe("Se espera end de la funcion", numInstruccion, tokenActual);
		sigToken();
		if((tokenActual.cat != CategoriaLexica.CLID))
			error.escribe("Se espera el id de la funcion", numInstruccion, tokenActual);
		
		if(!tokenActual.lex.equals(tokenId.lex))
			error.escribe("Se espera el id de la funcion", numInstruccion, tokenActual);
		sigToken();
		if(!(tokenActual.cat==CategoriaLexica.CLPUNTOCOMA))
			error.escribe("Falta punto y coma", numInstruccion, tokenActual);
		
		
	}
	private Tipos esTipoRet(){
		Tipos t = Tipos.error;
		if(tokenActual.cat == CategoriaLexica.CLTINT)
			t = Tipos.entero;
		else if(tokenActual.cat == CategoriaLexica.CLTREAL)
			t = Tipos.real;
		else if (tokenActual.cat == CategoriaLexica.CLPOINTER)
			t = Tipos.puntero;
		return t;
	}
	private void decParam(TablaAuxiliar tPar,TablaSimbolos tsPar,ArrayList<String> al){
		sigToken();
		if(tokenActual.cat != CategoriaLexica.CLPC){
			param(tPar,tsPar,al);
			if(tokenActual.cat == CategoriaLexica.CLCOMA){
				
				decParam(tPar,tsPar,al);
			}
		}
	}
	private void param(TablaAuxiliar tPar,TablaSimbolos tsPar,ArrayList<String> al){
		CategoriaLexica c = tokenActual.cat;
		if(c == CategoriaLexica.CLID){
			if(!t.existeId(tokenActual.lex))
				error.escribe("No existe el tipo", numInstruccion, tokenActual);
			Tipos tip = ts.tipoDe(tokenActual.lex);
			al.add(tokenActual.lex);
			int posi = ts.damePos(tokenActual.lex);
			RegistroTS reg = new RegistroTS(tip,numDirPar,Clase.variable,ts.dameEst(tokenActual.lex),posi);
			RegAux rega = new RegAux(numDirPar,tip,t.getReg(tokenActual.lex).getTs());
			numDirPar = numDirPar + posi;
			sigToken();
			if(tokenActual.cat == CategoriaLexica.CLAMPERSAN){
				sigToken();
			}
			if(tokenActual.cat != CategoriaLexica.CLID)
				error.escribe("Se espera un id en el parametro", numInstruccion, tokenActual);
			if(tsPar.existeId(tokenActual.lex)||ts.existeIdTipo(tokenActual.lex)||ts.existeId(tokenActual.lex))
				error.escribe("Parametro repetido", numInstruccion, tokenActual);
			tsPar.anadeId(tokenActual.lex, reg);
			tPar.anade(tokenActual.lex, rega);
			sigToken();
		}
		else if(c == CategoriaLexica.CLTINT){
			Tipos tip = Tipos.entero;
			RegistroTS reg = new RegistroTS(tip,numDirPar,Clase.variable,null,1);
			numDirPar++;
			al.add("int");
			sigToken();
			if(tokenActual.cat != CategoriaLexica.CLID)
				error.escribe("Se espera un id en el parametro", numInstruccion, tokenActual);
			if(tsPar.existeId(tokenActual.lex)||ts.existeIdTipo(tokenActual.lex)||ts.existeId(tokenActual.lex))
				error.escribe("Parametro repetido", numInstruccion, tokenActual);
			tsPar.anadeId(tokenActual.lex, reg);
			RegAux rega = new RegAux(numDir,Tipos.entero,null);
			tPar.anade(tokenActual.lex, rega);
			
			sigToken();
		}
		else if(c == CategoriaLexica.CLTREAL){
			Tipos tip = Tipos.real;
			RegistroTS reg = new RegistroTS(tip,numDirPar,Clase.variable,null,1);
			numDirPar++;
			al.add("real");
			sigToken();
			if(tokenActual.cat != CategoriaLexica.CLID)
				error.escribe("Se espera un id en el parametro", numInstruccion, tokenActual);
			if(tsPar.existeId(tokenActual.lex)||ts.existeIdTipo(tokenActual.lex)||ts.existeId(tokenActual.lex))
				error.escribe("Parametro repetido", numInstruccion, tokenActual);
			tsPar.anadeId(tokenActual.lex, reg);
			RegAux rega = new RegAux(numDir,Tipos.entero,null);
			tPar.anade(tokenActual.lex, rega);
			
			sigToken();
		}
			
	}
	private String dameRef(String s){
		int ind = s.indexOf(";");
		String aux = s.substring(2, ind);
		if(aux.equals("entero")) aux = "int";
		else if(aux.equals("real")) aux = "real";
		else{
			ind = s.indexOf(":",ind)+1;
			aux = s.substring(ind, s.length());
		}
		return aux;
	}
    private void variable(){
    	pos = 0;
    	ArrayList<Object> aL = tipado();
		String id = tokenActual.lex;
		Token t = tokenActual;
		Tipos tip = dameT((String)aL.get(0));
		String s = dameRef((String)aL.get(0));
		if(tip == Tipos.error){
			error.escribeSemantico("Tipo no existente", numInstruccion, t);
		}
		RegistroTS reg = new RegistroTS(tip,numDir,Clase.variable,aL,pos,s);
		if (tip == Tipos.puntero) numDir++;
		else numDir = numDir+pos;
		numInstruccion++;
		if(ts.anadeId(id, reg)==null){
			error.escribe("Variable ya existente",numInstruccion,tokenActual);
		}
		
		ArrayList<Object> al = ts.dameEst(id);
		convDec(al,id);
		String tips = ts.dameTipoRef(id);
		if(dameT((String)al.get(0))==Tipos.puntero)
			genCodigo.genDecVarPun(tips,id , ts.dirDe(id),ts.damePos(id));
		else
			genCodigo.genDecVar(tips,id , ts.dirDe(id),ts.damePos(id));
		sigToken();
		if(!(tokenActual.cat==CategoriaLexica.CLPUNTOCOMA))
			error.escribe("Falta punto y coma", numInstruccion, t);
    }
	private void tipoConstruido(){
		//tipoConstruido -> Descripcion tipo id ;
		pos = 0;
		sigToken();
		ArrayList<Object> aL = tipado();
		Token t = tokenActual;
		if(!(tokenActual.cat == CategoriaLexica.CLID))
			error.escribeSemantico("fallo", numInstruccion, tokenActual);
		String id = tokenActual.lex;
		Tipos tip = dameT((String)aL.get(0));
		RegistroTS reg = new RegistroTS(tip,numDir,Clase.tipo,aL,pos);
		numInstruccion++;
		if(ts.anadeId(id, reg)==null){
			error.escribeSemantico("Variable ya existente",numInstruccion,tokenActual);
		}
		sigToken();
		ArrayList<Object> al = ts.dameEst(id);
		conv(al,id,false);

		if(!(tokenActual.cat==CategoriaLexica.CLPUNTOCOMA))
			error.escribeSemantico("Falta punto y coma", numInstruccion, t);
	}
	private ArrayList<Object> tipado(){
		
		String s = descripcionTipo();
		ArrayList<Object> aL = new ArrayList<Object>();
		if(s.equals("rec")){
			aL = registro();
		}	
		else if (s.equals("puntero")){
			aL = puntero();
		}
		else if(s.equals("array")){
			aL = array();
		}
		else if(s.equals("tipDec")){
			aL = tipoDeclarado();
		}
		else if(s.equals("tipBas"))
			aL = tipoBasico();
		else
			error.escribe("Fallo de tipo", numInstruccion, tokenActual);
		return aL;
	}
	private ArrayList<Object> registro(){
		//registro -> Rec ListaCamposOb endrec
		ArrayList<Object> aL = new ArrayList<Object>();
		String s = "t:rec;campos:";
		aL.add(s);
		sigToken();
		ArrayList<Object> a = listaCamposOb();
		if(!esFinRec()){
			error.escribe("Se espera finrec para acabar el registro", numInstruccion, tokenActual);
		}
		sigToken();
		aL.add(a);
		return aL;
	}
	private ArrayList<Object> listaCamposOb(){
		ArrayList<Object> aL = new ArrayList<Object>();
		if(!descripcionTipo().equals("error")){
			ArrayList<Object> a = campo();
			aL.add(a);
			a = listaCampos(aL);
		}
		else
			error.escribe("Es necesario un campo en el registro", numInstruccion, tokenActual);//debe haber un campo por lo menos
		return aL;
	}
	private ArrayList<Object> listaCampos( ArrayList<Object> tip){
		//ListaCampos -> Campo ListaCampos
		ArrayList<Object> a = campo();
		ArrayList<Object> t = tip;
		if(a!=null)
			tip.add(a);	
			if(!esFinRec()){
				t = listaCampos(tip);
		}
		return t;
	}
	private ArrayList<Object> campo(){
		//Campo -> DescripcionTipo id ;
		ArrayList<Object> aL = new ArrayList<Object>();
		if(!esFinRec()){
			ArrayList<Object> a = tipado();
			String id = tokenActual.lex;
			if(!(tokenActual.cat==CategoriaLexica.CLID))
				error.escribeSemantico("fallo", numInstruccion, tokenActual);
			sigToken();
			if(!(tokenActual.cat==CategoriaLexica.CLPUNTOCOMA)){
				error.escribeSemantico("fallo", numInstruccion, tokenActual);
			}
			String s = "id:"+id+";tipo:";
			aL.add(s);
			aL.add(a);
			sigToken();
			return aL;
		}
		return null;
	}
	private ArrayList<Object> tipoDeclarado(){
		ArrayList<Object> aL = new ArrayList<Object>();
		String s = "t:ref;id:"+tokenActual.lex;
		aL.add(s);
		
		if(existeTipoEnTS(tokenActual.lex)){
			int n= this.nivelIdTipo(tokenActual.lex);
			
			pos = pos + tabla.get(n).getTs().posicionesDe(tokenActual.lex);
			
		}
		else
			error.escribeSemantico("Tipo no construido",numInstruccion,tokenActual);
		sigToken();
		
		return aL;
	}
	private ArrayList<Object> tipoBasico(){
		ArrayList<Object> aL = new ArrayList<Object>();
		Tipos tip = null;
		decTipo = tokenActual.lex;
		if(decTipo.equals("int")) tip = Tipos.entero;
		else if(decTipo.equals("real")) tip = Tipos.real;
		String s = "t:"+tip.toString()+";";
		aL.add(s);
		pos = pos + 1;
		sigToken();
		return aL;
	}
	private ArrayList<Object> puntero(){
		ArrayList<Object> aL = new ArrayList<Object>();
		String s = "t:puntero;tbase:";
		aL.add(s);
		sigToken();
		ArrayList<Object> a = tipado();
		if(descripcionTipo().equals("error"))
			error.escribeSemantico("fallo", numInstruccion, tokenActual);
		aL.add(a);
		
		return aL;
				
	}
	private ArrayList<Object> array(){
		ArrayList<Object> aL = new ArrayList<Object>();
		ArrayList<Object> a = null;
		boolean b = false;
		if(esTipoBasico()){
			a = tipoBasico();
			b = true;
		}
		else if (esTipoDeclarado()){
			a = tipoDeclarado();
		}
		else
			error.escribe("fallo", numInstruccion, tokenActual);
		//corchetes abiertos
		sigToken();
		//dimension del array4
		int numElems = Integer.parseInt(tokenActual.lex);
		if(numElems <= 0)
			error.escribe("fallo", numInstruccion, tokenActual);
		if(b) pos = pos +numElems-1;
		else pos = pos *numElems;
		String s = "t:array;nElems"+tokenActual.lex+";tbase:";
		aL.add(s);
		aL.add(a);
	
		sigToken();
		//corchetes cerrados
		sigToken();
		return aL;

	}
	
	private String descripcionTipo(){
		String s = "error";
		if(esRegistro()){
			s = "rec";		
		}	
		else if (esPuntero()){
			s = "puntero";
		}
		else if(esArray()){
			s = "array";
		}
		else if(esTipoDeclarado()){
			s = "tipDec";
		}
		else if(this.esTipoBasico())
			s="tipBas";
		return s;
		
	}
	//mira si es de algun tipo aceptado por el lenguaje
	private boolean matchTipo(){
		decTipo = tokenActual.lex;
		boolean b = tokenActual.cat == CategoriaLexica.CLTINT || tokenActual.cat == CategoriaLexica.CLTREAL 
					|| tokenActual.cat == CategoriaLexica.CLTIPO||
					((tokenActual.cat == CategoriaLexica.CLID)&&(!existeEnTS(tokenActual.lex)) 
							||(tokenActual.cat == CategoriaLexica.CLFUN));
		return b;
	}
	private Tipos dameT(String s){
		int ind = s.indexOf(";");
		String aux = s.substring(2, ind);
		Tipos t = Tipos.error; 
		if(aux.equals("puntero")) t = Tipos.puntero;
		else if (aux.equals("array")) t = Tipos.array;
		else if (aux.equals("entero"))	t = Tipos.entero;
		else if (aux.equals("real"))	t = Tipos.real;
		else if (aux.equals("rec"))	t = Tipos.registro;
		else if (aux.equals("ref")){	
			ind = s.indexOf(":", ind);
			aux = s.substring(ind+1,s.length());
			int n = nivelIdTipo(aux);
			t = tabla.get(n).getTs().tipoDe(aux);
		}
		return t;
	}
	//mira si es de la categoria ID
	private boolean match(CategoriaLexica c){

		return (tokenActual.cat == c);
	}
	private boolean esTipoBasico(){
		
		return (tokenActual.cat == CategoriaLexica.CLTINT || tokenActual.cat == CategoriaLexica.CLTREAL);
	}
	private boolean esFuncion(){
		return tokenActual.cat == CategoriaLexica.CLFUN;
	}
	private boolean esDeclaracion(){
		return esTipoBasico() || esTipoDeclarado();
	}
	private boolean esTipo(){
		return tokenActual.cat == CategoriaLexica.CLTIPO;
	}
	private boolean esRegistro(){
		return tokenActual.cat == CategoriaLexica.CLREC;
	}
	private boolean esFinRec(){
		return tokenActual.cat == CategoriaLexica.CLENDREC;
	}
	private boolean esPuntero(){
		return(tokenActual.cat == CategoriaLexica.CLPOINTER);
	}
	private boolean esTipoDeclarado(){
		return tokenActual.cat == CategoriaLexica.CLID;//hay que hacerlo
	}
	private boolean esArray(){
		//falta poner para tipos construidos
		boolean b = esDeclaracion() && lista.mirarToken(1).cat == CategoriaLexica.CLCORCHETEAB
				|| lista.mirarToken(2).cat == CategoriaLexica.CLENT || lista.mirarToken(3).cat == CategoriaLexica.CLCORCHETECER;
		return b;
	}
	
	
	
	
	
	
	
	/******************************************************************************
	 ****************DECLARACION DE ACCIONES**************************************
	*******************************************************************************/
	
	
	private void SAc(){
		//SAc -> Ac RSA
		//SAc -> Ac
		//comprobamos que hay una primera accion, sino publicamos el error
		if (tokenActual.cat == CategoriaLexica.CLEND || tokenActual.cat == CategoriaLexica.CLEOF){
			error.escribe("No hay primera accion",numInstruccion,tokenActual);
		}
		Ac();//procesamos la accion y comprobamos que termina en ; sino, error
		if(tokenActual.cat == CategoriaLexica.CLPUNTOCOMA){
			sigToken();
			numInstruccion++;//sumamos una instruccion
			RSA();//vemos si hay mas acciones
		}
		else{
			error.escribe("Falta punto y coma",numInstruccion,tokenActual);
		}
		
			
	}
	//metodo para procesar acciones
	private void RSA(){
		if (tokenActual.cat != CategoriaLexica.CLEND && tokenActual.cat != CategoriaLexica.CLEOF){//miramos si no se ha acabado el codigo
			Ac();//procesamos la accion
			if(tokenActual.cat == CategoriaLexica.CLPUNTOCOMA){//vemos que termina correctamente la instruccion
				pila.vaciaPila();//vaciamos la pila de tipos, para la siguiente instruccion
				sigToken();
				numInstruccion++;//sumamos una instruccion
				RSA();//procesamos las siguientes acciones
			}
			else{
				error.escribe("Falta punto y coma",numInstruccion,tokenActual);
			}
		}
	}
	private void Ac(){

		if (esIf())
			acIf();
		else if (esWhile())
			acWhile();
		else if (esAlloc()||esFree())
			funMem();
		else if(esReturn())
			acReturn();
		else
			Exp();//procesamos la expresion
		
		
	}
	private void acReturn(){
		sigToken();
		Exp();
		Tipos tip = pila.desapila();
		genCodigo.desapilaDirBloque(1, nivel);
		if( !puedeRet(tip))
			error.escribeSemantico("Error de tipo retornado",numInstruccion,tokenActual);
		genCodigo.apilaDirBloque(1, nivel);
		genCodigo.generaVolver(nivel);
		pila.apila(tip);
		
	}
	private boolean puedeRet(Tipos tp){
		if((tReturn == Tipos.entero)&&(tp == Tipos.real))
			return false;
		else
			return true;
			
	}
	private boolean esReturn(){
		return tokenActual.cat == CategoriaLexica.CLRETURN;
	}
	//expresion de prioridad 0 ( in y out)
	private void Exp(){
		//Exp -> in variable
		//Exp -> out Exp
		//Exp -> Exp1
		if(match(CategoriaLexica.CLIN)){
			sigToken();
			//comprobamos que a continuacion viene un id, sino es error
			if (match(CategoriaLexica.CLID)){
				//comprobamos que exite el id, sino, error
				ArrayList<Object> al = MemAsig();
				int dir = (Integer)al.get(0);
				int n = (Integer) al.get(1);
				Tipos tipo = tipoMem;
				if(al.size() == 2)
					if(n==0)
						genCodigo.generaIn(dir, ant);
					else
						genCodigo.generaInBloque(dir, ant,n);
				else
					if(n == 0)
						genCodigo.generaInInd(dir, (Integer)al.get(2), ant);
					else 
						genCodigo.generaInIndBloque(dir, (Integer)al.get(2), ant,n);

				pila.apila(tipo);//apilamos el tipo del la variable que pedimos por pantalla

				if(al.size() == 2){
					if(n==0)
						genCodigo.apilaDir(dir, ant);
					else
						genCodigo.apilaDirBloque(dir,ant,n);
				}
				else{
					if (n==0)
						genCodigo.apilaInd(dir, (Integer)al.get(2), ant);
					else
						genCodigo.apilaIndBloque(dir, (Integer)al.get(2), ant,n);
				}
				sigToken();

			}
			else{
				error.escribe("Se espera variable",numInstruccion,tokenActual);
			}
			
		}
		else if (match(CategoriaLexica.CLOUT)){
			int direccion =-1;//inicializamos la direccion a -1, por si el out no es sobre un id.
			if(lista.mirarToken(1).cat == CategoriaLexica.CLID) { //comprobamos si es un id
				
				if (!existeEnTS((lista.mirarToken(1).lex)))//vemos que exista el id, sino error
					error.escribeSemantico("Variable no existente",numInstruccion, tokenActual);
				
				direccion = 1;	//guardamos la direccion del id
			}	
			sigToken();
			Exp();//procesamos lo que viene acontinuacion
			genCodigo.generaOut(direccion);//generamos el out, despues de procesar toda la expresion
			
		}
		
		else{
			Exp1();//Pasamos de prioridad
		}
	}
	private void Exp1(){
		//Exp -> id RExp1
		//Exp -> Exp2
		if((tokenActual.cat == CategoriaLexica.CLID)&&(lista.hayAsig())){
			
			ArrayList<Object> al = MemAsig();
			int direccion = (Integer)al.get(0);
			Tipos tipo = tipoMem;
			Direcciones d;
			String ante = ant;
			if(al.size() == 2)
				d = new Direcciones(direccion,-1,nivel);
			else
				d = new Direcciones(direccion,(Integer)al.get(1),nivel);
			int niv = (Integer)al.get(1);
			Usadas usadas = new Usadas();//creamos la estructura usadas para poder ver que no se duplica la variable
			usadas.inserta(d);//insertamos la variable leida
			sigToken();
			Token tOp = tokenActual;//guardamos el token actual, para el error
			CategoriaLexica operacion = tokenActual.cat;//guardamos la operacion para comprobar el tipado
			RExp1(usadas);//procesamos lo que sigue de expresion pasando la variable de variables usadas
			if(tokenActual.cat != CategoriaLexica.CLPUNTOCOMA){
				if(esOpExp2())
					RExp2();
				else if(esOpExp3())
					RExp3();
				else if(esOpExp4())
					RExp4();
			}
			tipo = tablas.daResultado(operacion, tipo, pila.desapila());//comprobamos el tipado
			if(tipo == Tipos.error)  error.escribeSemantico("Error de tipado",numInstruccion,tOp);
			pila.apila(tipo);//apila el tipo
			if(al.size() == 2){
				if(niv!=0){
					genCodigo.desapilaDirBloque(direccion, ante,niv);
				}
				else
					genCodigo.desapilaDir(direccion, ante);
			}
			else{
				if(niv!=0)
					genCodigo.desapilaIndBloque(direccion, (Integer)al.get(2), ante,niv);
				else
					genCodigo.desapilaInd(direccion, (Integer)al.get(2), ante);
			}
			
				
		}
		else
			Exp2();
	}
	private void RExp1(Usadas usadas){
		//RExp1 -> RExp2
		//RExp1 -> RExp3
		//RExp1 -> RExp4
		//RExp1 -> = RestoExp1
		//RExp1 -> *
		if(esOpExp2())
			RExp2();
		else if(esOpExp3())
			RExp3();
		else if(esOpExp4())
			RExp4();
		else if(tokenActual.cat == CategoriaLexica.CLASIG){
			sigToken();
			RestoExp1(usadas);
		}
			
	}
	
	private void RestoExp1(Usadas usadas){
		//RestoExp1 -> id RExp1
		//RestoExp -> Exp2
		if (tokenActual.cat == CategoriaLexica.CLID){
			if(tf.existeId(tokenActual.lex)){
				this.invocarFun();
				sigToken();
			}
			else{
			ArrayList<Object> al = MemAsig();
			int direccion = (Integer)al.get(0);
			int niv = (Integer)al.get(1);
			String ante = ant;
			Tipos tipo = tipoMem;
			Token tOp = tokenActual;//guardamos el token por si se genera un error
			//genCodigo.apilaDir(direccion);//generamos el codigo para apilar el valor
			if(al.size() == 2){
				if(niv==0)
					genCodigo.apilaDir(direccion, ant);
				else
					genCodigo.apilaDirBloque(direccion,ant,niv);
			}
			else{
				if (niv==0)
					genCodigo.apilaInd(direccion, (Integer)al.get(2), ant);
				else
					genCodigo.apilaIndBloque(direccion, (Integer)al.get(2), ant,niv);
			}
			pila.apila(tipo);//apilamos el tipo
			sigToken();
			CategoriaLexica operacion = null;
			if(tokenActual.cat == CategoriaLexica.CLASIG) {//vemos si hay asignacion o no
				operacion = tokenActual.cat;				
			}
			Direcciones d;
			if(al.size() == 2)
				d = new Direcciones(direccion,-1,nivel);
			else
				d = new Direcciones(direccion,(Integer)al.get(1),nivel);
			
			if (((tokenActual.cat == CategoriaLexica.CLPUNTOCOMA)||(tokenActual.cat == CategoriaLexica.CLASIG))
				&&(!usadas.inserta(d)))//comprobamos que la variable no ha sido usada antes si va a ver mas asignaciones
						error.escribeSemantico("Variable ya usada en la asignacion",numInstruccion,tOp);
			RExp1(usadas);//procesamos lo que queda de expresion
			if (operacion != null){
				tipo = tablas.daResultado(CategoriaLexica.CLASIG, tipo, pila.desapila());//comprobamos el tipado
				if(tipo == Tipos.error)  error.escribeSemantico("Error de tipado",numInstruccion,tOp);
				pila.apila(tipo);//apilamos el tipo
				
				if(al.size() == 2){
					if(niv!=0){
						genCodigo.desapilaDirBloque(direccion, ante,niv);
					}
					else
						genCodigo.desapilaDir(direccion, ante);
				}
				else{
					if(niv!=0)
						genCodigo.desapilaIndBloque(direccion, (Integer)al.get(1), ante,niv);
					else
						genCodigo.desapilaInd(direccion, (Integer)al.get(1), ante);
				}
			}
			}
		}
		else
			Exp2();//procesamos la expresion
	}
	
	private ArrayList<Object> MemAsig(){
		dirMem = 0;
		ant = "";
		ArrayList<Object> al = new ArrayList<Object>();
		Token tokenId = tokenActual;
		if (!existeEnTS(tokenId.lex))//comprobamos que exite la variable
			error.escribe("Variable no existente", numInstruccion, tokenId);
		if(esDesignador(lista.mirarToken(1).cat)){
			sigToken();
			CategoriaLexica c = tokenActual.cat;
			if(esCircunflejo(c)){
				int n = nivelIdVar(tokenId.lex);
				if(tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTipo()!= Tipos.puntero)
					error.escribe("Esta variable no es un puntero", numInstruccion, tokenId);
				//TablaAuxiliar taa = tss.getReg(tokenId.lex).getTs();
				
				TablaAuxiliar taa = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
				tipoMem = taa.getReg("^").getTipo();
				TablaAuxiliar taba = taa.getReg("^").getTs();
				RMemAsig(taba);

				al.add(tabla.get(n).getTs().dirDe(tokenId.lex));
				al.add(n);
				al.add(dirMem);
			
			}
			else if(esPunto(c)){
				sigToken();
				if(tokenActual.cat != CategoriaLexica.CLID)
					error.escribe("Se espera un id",numInstruccion,tokenActual);
				
				//TablaAuxiliar taa = tss.getReg(tokenId.lex).getTs();
				int n = nivelIdVar(tokenId.lex);
				TablaAuxiliar taa = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
				if (!taa.existeId(tokenActual.lex))//comprobamos que exite la variable
					error.escribe("Variable no existente", numInstruccion, tokenActual);
				dirMem += taa.getReg(tokenActual.lex).getNumDir();
				if(taa.getReg(tokenActual.lex).isPunt())  tipoMem = Tipos.puntero;
				else 	tipoMem = taa.getReg(tokenActual.lex).getTipo();
				RMemAsig(taa.getReg(tokenActual.lex).getTs());
				
				int numeroDir = dirMem +tabla.get(n).getTs().dirDe(tokenId.lex);
				al.add(numeroDir);
				al.add(n);

				
				
			}
			else if(esCorAbierto(c)){
				sigToken();
				Exp();
				Tipos tip = pila.desapila();
				if(tip != Tipos.entero)
					error.escribeSemantico("El indice del array debe ser un entero",numInstruccion,tokenActual);
				if(!esCorCerrado(tokenActual.cat))//comprobamos que se cierra el parentesis, sino, error
					error.escribe("No cierre de corchetes",numInstruccion,tokenActual);

				int n = nivelIdVar(tokenId.lex);
				TablaAuxiliar taa = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
				if(!taa.existeId("0"))
					error.escribe("No es de tipo array",numInstruccion,tokenId);
				TablaAuxiliar taba = taa.getReg("0").getTs();
				ant +=",s," + taa.getReg("0").getNumElems();
				if(taa.getReg("0").isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = taa.getReg("0").getTipo();
				RMemAsig(taba);
			
				
				int numeroDir = dirMem +tabla.get(n).getTs().dirDe(tokenId.lex);

				al.add(numeroDir);
				al.add(n);
			}
		}
		else{		
			
			tipoMem = dameTipo();
			int n = nivelIdVar(tokenActual.lex);
			al.add(tabla.get(n).getTs().dirDe(tokenId.lex));
			al.add(n);
		}
		return al;
	}
	private void RMemAsig(TablaAuxiliar ta){
		if(esDesignador(lista.mirarToken(1).cat)){
			sigToken();
			CategoriaLexica c = tokenActual.cat;
			if(esCircunflejo(c)){
						
				RMemAsig(ta);
			}
			else if(esPunto(c)){
				sigToken();
				
				if(tokenActual.cat != CategoriaLexica.CLID)
					error.escribe("Se espera un id",numInstruccion,tokenActual);
				
				TablaAuxiliar taa = ta.getReg(tokenActual.lex).getTs();
				if (!ta.existeId(tokenActual.lex))//comprobamos que exite la variable
					error.escribe("Variable no existente", numInstruccion, tokenActual);
				dirMem += ta.getReg(tokenActual.lex).getNumDir();
				if(ta.getReg(tokenActual.lex).isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = ta.getReg(tokenActual.lex).getTipo();

				RMemAsig(taa);
			}
			else if(esCorAbierto(c)){
				sigToken();
				Exp();
				Tipos tip = pila.desapila();
				if(tip != Tipos.entero)
					error.escribeSemantico("El indice del array debe ser un entero",numInstruccion,tokenActual);
				if(!esCorCerrado(tokenActual.cat))//comprobamos que se cierra el parentesis, sino, error
					error.escribe("No cierre de corchetes",numInstruccion,tokenActual);
				
				if(!ta.existeId("0"))
					error.escribe("No es de tipo array",numInstruccion,tokenActual);
				RegAux r= ta.getReg("0");
				ant +=",s," + ta.getReg("0").getNumElems();
				TablaAuxiliar tab = r.getTs();
				if(r.isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = r.getTipo();
				RMemAsig(tab);	
			}
		}
	}	
	
	
	
	
	private void Exp2(){
		//Exp2 -> Exp3 RExp2
		Exp3();
		RExp2();
		
	}
	
	private void RExp2(){
		//RExp2 -> opExp2 Exp3 RExp2
		if (esOpExp2()){
			CategoriaLexica operacion = tokenActual.cat;//guardamos el tipo de operacion
			String op = tokenActual.lex;//guardamos el lexema
			Token tOp = tokenActual;//guardamos el token actual por si hay fallo
			sigToken();
			Exp3();//seguimos procesando la expresion
			genCodigo.generaOp(operacion,op);//genera el codigo de la operacion correspondiente
			RExp2();
			Tipos tipo = tablas.daResultado(operacion, pila.desapila(), pila.desapila());//comprobamos el tipado
			if(tipo == Tipos.error)  error.escribeSemantico("Error de tipado",numInstruccion,tOp);
			pila.apila(tipo);//apila el tipo
		
		}
	}
	
	private void Exp3(){
		//Exp3 -> Exp4 RExp3
		Exp4();
		RExp3();
	}
	
	private void RExp3(){
		//RExp3 -> opExp3 Exp4 RExp3
		if(esOpExp3()){
				String op= tokenActual.lex;//guardamos el lexema
				CategoriaLexica operacion = tokenActual.cat;//guardamos el tipo de operacion
				Token tOp = tokenActual;//guardamos el token actual por si hay error
				sigToken();
				Exp4();//procesamos lo que queda de expresion
				genCodigo.generaOp(operacion,op);//genera el codigo de la operacion correspondiente
				RExp3();
				Tipos tipo = tablas.daResultado(operacion, pila.desapila(), pila.desapila());//comprobamos el tipado
				if(tipo == Tipos.error) error.escribeSemantico("Error de tipado",numInstruccion,tOp);
				pila.apila(tipo);//apila el tipo
				
		}
		
	}
	
	private void Exp4(){
		//Exp4 -> Exp5 RExp4
		Exp5();
		RExp4();
	}
	
	private void RExp4(){
		//RExp4 -> opExp4 Exp5 RExp4
		if(esOpExp4()){
				String op= tokenActual.lex;//guardamos el lexema
				CategoriaLexica operacion = tokenActual.cat;//guardamos el tipo de operacion
				Token tOp = tokenActual;//guardamos el token actual por si ocurre un error
				sigToken();
				Exp5();//seguimos procesando la expresion restante
				genCodigo.generaOp(operacion,op);//genera el codigo de la operacion correspondiente
				RExp4();
				Tipos tipo = tablas.daResultado(operacion, pila.desapila(), pila.desapila());
				if(tipo == Tipos.error) error.escribeSemantico("Error de tipado",numInstruccion,tOp);
				pila.apila(tipo);// apila el tipo
				
		}
		
	}
	private void Exp5(){
		//Exp5 -> - Exp5
		//Exp5 -> ! Exp5
		//Exp5 -> (int) Exp6
		//Exp5 -> (real) Exp6
		//Exp5 -> Exp6
		if(tokenActual.cat == CategoriaLexica.CLRESTA || tokenActual.cat == CategoriaLexica.CLNEG){
			String op = tokenActual.lex;//guardamos el lexema
			CategoriaLexica operacion = tokenActual.cat;//guardamos el tipo de operacion
			Token tOp = tokenActual;//guardamos el token actual por si hay error
			sigToken();
			Exp5();//procesamos lo que queda de expresion
			Tipos tipo = tablas.daResultado(operacion, pila.desapila());//comprobamos el tipado
			if(tipo == Tipos.error)  error.escribeSemantico("Error de tipado",numInstruccion,tOp);
			pila.apila(tipo);//apilamos el tipo resultante
			genCodigo.generaOpUnario(op);//generamos el codigo para la operacion
		}
		else if(tokenActual.cat == CategoriaLexica.CLCASTREAL || tokenActual.cat == CategoriaLexica.CLCASTENT){
			String op = tokenActual.lex;//guardamos el lexema
			CategoriaLexica operacion = tokenActual.cat;//guardamos el tipo de operacion
			Token tOp = tokenActual;//guardamos el token actual por si hay error
			sigToken();
			Exp6();// procesamos la expresion restante
			Tipos tipo = tablas.daResultado(operacion, pila.desapila());//comprobamos el tipado
			if(tipo == Tipos.error)  error.escribeSemantico("Error de tipado",numInstruccion,tOp);
			pila.apila(tipo);//apilamos el tipo resultante
			genCodigo.generaOpCast(op);//generamos el codigo de la operacion correspondiente
		}
		else{
			Exp6();
		}
		
	}
	private void Exp6(){
		if(tokenActual.cat == CategoriaLexica.CLENT){
			genCodigo.apila(tokenActual.lex);//generamos el codigo de apilar un valor
			pila.apila(Tipos.entero);//apilamos el tipo
		}
		else if(tokenActual.cat == CategoriaLexica.CLREAL){
			genCodigo.apila(tokenActual.lex);//generamos el codigo de apilar un valor
			pila.apila(Tipos.real);//apilamos el tipo
		}
		else if(tokenActual.cat == CategoriaLexica.CLID){
			Mem();
	
		}
		else if(tokenActual.cat == CategoriaLexica.CLNULL){
			genCodigo.apila(tokenActual.lex);//generamos el codigo de apilar un valor
			pila.apila(Tipos.puntero);//apilamos el tipo
		}
		else if(tokenActual.cat == CategoriaLexica.CLPA){
			
			sigToken();
			Exp();//procesamos lo que resta de expresion
			if(tokenActual.cat != CategoriaLexica.CLPC){//comprobamos que se cierra el parentesis, sino, error
				error.escribe("No cierre de parentesis",numInstruccion,tokenActual);
			}
		}
		else{
			error.escribe("Caracter no esperado",numInstruccion,tokenActual);
		}
		if(tokenActual.cat != CategoriaLexica.CLEOF)			
			sigToken();
	}

	private void invocarFun(){
		
		
		Token tokenId = tokenActual;
		if(tokenActual.cat != CategoriaLexica.CLID)
			error.escribe("Se espera un ID", numInstruccion, tokenActual);
		String id = tokenActual.lex;
		if(!tf.existeId(id))
			error.escribe("No existe la funcion", numInstruccion, tokenActual);
		sigToken();
		if(tokenActual.cat != CategoriaLexica.CLPA)
			error.escribe("Se espera un parentesis abierto", numInstruccion, tokenActual);
		sigToken();
		ArrayList<String> al = new ArrayList<String>();
		leerParam(al);
		ArrayList<String> alFun = tf.getReg(tokenId.lex).getParametros();
		
		if(tokenActual.cat != CategoriaLexica.CLPC)
			error.escribe("Se espera un parentesis cerrado", numInstruccion, tokenActual);
		if(al.size() != alFun.size())
			error.escribeSemantico("La funcion no tiene el numero de parametros correctos", numInstruccion, tokenId);
		ArrayList<String> param = convTipos(al);
		comprobarTipado(alFun,param);
		apilarParametros(al);
		int etq = genCodigo.getEtq();
		etq = etq+2;
		genCodigo.apila(Integer.toString((etq)));
		genCodigo.ira(tf.getReg(tokenId.lex).getDir()-1);
		pila.apila(tf.getReg(tokenId.lex).getRetorno());
	
		
	}
	private void apilarParametros(ArrayList<String> al){

		for(int i = 0; i<al.size();i++){
			String id = al.get(i);
			int mem = ts.dirDe(id);
			int posi = ts.damePos(id);
			for (int j= mem;j<mem+posi;j++)
				genCodigo.apilaDir(j);
		}
	}
	private void comprobarTipado(ArrayList<String> al1,ArrayList<String>al2){
		for(int i = 0;i<al1.size();i++){
			if(!(al1.get(i).equals(al2.get(i))))
				error.escribeSemantico("Error de tipos en los parametros", numInstruccion, tokenActual);
		}
	}
	private ArrayList<String> convTipos(ArrayList<String> al){
		ArrayList<String> at = new ArrayList<String>();
		for (int i = 0;i<al.size();i++){
			String tip = al.get(i);
			ArrayList<Object> all = ts.dameEst(tip);
			tip = dameRef((String)all.get(0));
			at.add(tip);
		}
		return at;
	}
	private void leerParam(ArrayList<String> al){
		if(tokenActual.cat != CategoriaLexica.CLPC){
			if(tokenActual.cat != CategoriaLexica.CLID)
				error.escribe("Se espera un ID", numInstruccion, tokenActual);
			al.add(tokenActual.lex);
			sigToken();
			if(tokenActual.cat == CategoriaLexica.CLCOMA)
				sigToken();
				leerParam(al);
		}
	}
	private void Mem(){
		dirMem = 0;
		ant = "";
		Token tokenId = tokenActual;
		if (!(existeEnTS(tokenId.lex)))//comprobamos que exite la variable
			error.escribe("Variable no existente", numInstruccion, tokenId);
		if(tf.existeId(tokenId.lex)){
			invocarFun();
		}
		else if(esDesignador(lista.mirarToken(1).cat)){
			sigToken();
			CategoriaLexica c = tokenActual.cat;
			if(esCircunflejo(c)){
				int n = nivelIdVar(tokenId.lex);;
				TablaAuxiliar tSS = tabla.get(n).getTablaVariables();
				if(tSS.getReg(tokenId.lex).getTipo()!= Tipos.puntero)
					error.escribe("Esta variable no es un puntero", numInstruccion, tokenId);
				TablaAuxiliar taa = tSS.getReg(tokenId.lex).getTs();
				tipoMem = taa.getReg("^").getTipo();
				TablaAuxiliar taba = taa.getReg("^").getTs();
				RMem(taba);
				TablaSimbolos tS = tabla.get(n).getTs();
				if(n!=0)
					genCodigo.apilaIndBloque(tS.dirDe(tokenId.lex),dirMem,ant,n);//generamos el codigo de apilar una direccion
				else
					genCodigo.apilaInd(tS.dirDe(tokenId.lex),dirMem,ant);//generamos el codigo de apilar una direccion
				pila.apila(tipoMem);//apilamos el tipo
			}
			else if(esPunto(c)){
				sigToken();
				if(tokenActual.cat != CategoriaLexica.CLID)
					error.escribe("Se espera un id",numInstruccion,tokenActual);
				int n = nivelIdVar(tokenId.lex);
				

				TablaAuxiliar taa = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
				if (!taa.existeId(tokenActual.lex))//comprobamos que exite la variable
					error.escribe("Variable no existente", numInstruccion, tokenActual);
				dirMem += taa.getReg(tokenActual.lex).getNumDir();
				if(taa.getReg(tokenActual.lex).isPunt())  tipoMem = Tipos.puntero;
				else 	tipoMem = taa.getReg(tokenActual.lex).getTipo();
				RMem(taa.getReg(tokenActual.lex).getTs());
				TablaSimbolos tAux = tabla.get(n).getTs();
				int numeroDir = dirMem +tAux.dirDe(tokenId.lex);
				if(n!=0){
					genCodigo.apilaDirBloque(numeroDir,ant, n);
				}
				else
					genCodigo.apilaDir(numeroDir,ant);//generamos el codigo de apilar una direccion
				pila.apila(tipoMem);//apilamos el tipo
				
				
			}
			else if(esCorAbierto(c)){
				sigToken();
				Exp();
				Tipos tip = pila.desapila();
				if(tip != Tipos.entero)
					error.escribeSemantico("El indice del array debe ser un entero",numInstruccion,tokenActual);
				if(!esCorCerrado(tokenActual.cat))//comprobamos que se cierra el parentesis, sino, error
					error.escribe("No cierre de corchetes",numInstruccion,tokenActual);
				int n = nivelIdVar(tokenId.lex);
				TablaAuxiliar taa = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
				
				if(!taa.existeId("0"))
					error.escribe("No es de tipo array",numInstruccion,tokenId);
				
				TablaAuxiliar taba = tabla.get(n).getTablaVariables().getReg(tokenId.lex).getTs();
		
				ant +=",s," + taa.getReg("0").getNumElems();
				if(taa.getReg("0").isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = taa.getReg("0").getTipo();
				RMem(taba);
			
				TablaSimbolos tS = tabla.get(n).getTs();
				int numeroDir = dirMem +tS.dirDe(tokenId.lex);
				if(n!=0){
					genCodigo.apilaDirBloque(numeroDir, ant,n);
				}
				else
					genCodigo.apilaDir(numeroDir,ant);//generamos el codigo de apilar una direccion
				pila.apila(tipoMem);//apilamos el tipo
			}
		}
		else{	
			int n = nivelIdVar(tokenId.lex);
			if(n!=0){
				
				genCodigo.apilaDirBloque(tabla.get(n).getTs().dirDe(tokenId.lex), n);
				pila.apila(tabla.get(n).getTablaVariables().tipoDe(tokenId.lex));
			}
			else{
			genCodigo.apilaDir(tabla.get(n).getTs().dirDe(tokenId.lex));//generamos el codigo de apilar una direccion
			pila.apila(tabla.get(n).getTablaVariables().tipoDe(tokenId.lex));//apilamos el tipo
			}
		}
	}
	private void RMem(TablaAuxiliar ta){
		if(esDesignador(lista.mirarToken(1).cat)){
			sigToken();
			CategoriaLexica c = tokenActual.cat;
			if(esCircunflejo(c)){
				//if()
						
				RMem(ta);
			}
			else if(esPunto(c)){
				sigToken();
				
				if(tokenActual.cat != CategoriaLexica.CLID)
					error.escribe("Se espera un id",numInstruccion,tokenActual);
				
				TablaAuxiliar taa = ta.getReg(tokenActual.lex).getTs();
				if (!ta.existeId(tokenActual.lex))//comprobamos que exite la variable
					error.escribe("Variable no existente", numInstruccion, tokenActual);
				dirMem += ta.getReg(tokenActual.lex).getNumDir();
				if(ta.getReg(tokenActual.lex).isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = ta.getReg(tokenActual.lex).getTipo();
				RMem(taa);
			}
			else if(esCorAbierto(c)){
				sigToken();
				Exp();
				Tipos tip = pila.desapila();
				if(tip != Tipos.entero)
					error.escribeSemantico("El indice del array debe ser un entero",numInstruccion,tokenActual);
				if(!esCorCerrado(tokenActual.cat))//comprobamos que se cierra el parentesis, sino, error
					error.escribe("No cierre de corchetes",numInstruccion,tokenActual);
				
				if(!ta.existeId("0"))
					error.escribe("No es de tipo array",numInstruccion,tokenActual);
				RegAux r= ta.getReg("0");
				ant +=",s," + ta.getReg("0").getNumElems();
				TablaAuxiliar tab = r.getTs();
				if(r.isPunt()) tipoMem = Tipos.puntero;
				else tipoMem = r.getTipo();
				RMem(tab);	
			}
		}
	}	
	private boolean esCircunflejo(CategoriaLexica c){
		return c == CategoriaLexica.CLCIRCUNFLEJO;
	}
	private boolean esPunto(CategoriaLexica c){
		return c == CategoriaLexica.CLPUNTO;
	}
	private boolean esCorAbierto(CategoriaLexica c){
		return c == CategoriaLexica.CLCORCHETEAB;
	}
	private boolean esCorCerrado(CategoriaLexica c){
		return c == CategoriaLexica.CLCORCHETECER;
	}
	private boolean esDesignador(CategoriaLexica c){
		return esCircunflejo(c)||esPunto(c)||esCorAbierto(c);
	}

	
	//getter del tipo de una variable
	private Tipos dameTipo(){
		int n = nivelIdVar(tokenActual.lex);
		if(tabla.get(n).getTablaVariables().getReg(tokenActual.lex).isPunt())
			return Tipos.puntero;
		return tabla.get(n).getTablaVariables().tipoDe(tokenActual.lex);
		
	}
	//comprueba si la operacion es de tipo de la exp2
	private boolean esOpExp2(){
		if (tokenActual.cat == CategoriaLexica.CLMENOR || tokenActual.cat == CategoriaLexica.CLMAYOR ||
				tokenActual.cat == CategoriaLexica.CLMENORIG || tokenActual.cat == CategoriaLexica.CLMAYORIG
				|| tokenActual.cat == CategoriaLexica.CLIGUAL || tokenActual.cat == CategoriaLexica.CLDISTINTO)
						return true;
		return false;
	}
	//comprueba si la operacion es de tipo de la exp3
	private boolean esOpExp3(){
		if(tokenActual.cat == CategoriaLexica.CLOR 
			|| tokenActual.cat == CategoriaLexica.CLSUMA 
		    || tokenActual.cat == CategoriaLexica.CLRESTA)
					return true;
		return false;
	}
	//comprueba si la operacion es de tipo de la exp4
	private boolean esOpExp4(){
		if(tokenActual.cat == CategoriaLexica.CLMUL
			|| tokenActual.cat == CategoriaLexica.CLDIV
			|| tokenActual.cat == CategoriaLexica.CLMOD
			|| tokenActual.cat == CategoriaLexica.CLAND)
					return true;
		return false;
	}
	
	private boolean esIf(){
		return tokenActual.cat == CategoriaLexica.CLIF;
	}
	
	private boolean esElse(){
		return tokenActual.cat == CategoriaLexica.CLELSE;
	}
	
	private boolean esElseIf(){
		return tokenActual.cat == CategoriaLexica.CLELSIF;
	}
	
	private boolean esEndIf(){
		return tokenActual.cat == CategoriaLexica.CLENDIF;
	}
	
	private boolean esThen(){
		return tokenActual.cat == CategoriaLexica.CLTHEN;
	}
	private boolean esAlloc(){
		return tokenActual.cat == CategoriaLexica.CLALLOC;
	}
	private boolean esFree(){
		return tokenActual.cat == CategoriaLexica.CLFREE;
	}
	private boolean esWhile(){
		return tokenActual.cat == CategoriaLexica.CLWHILE;
	}
	
	private boolean esDo(){
		return tokenActual.cat == CategoriaLexica.CLDO;
	}
	
	private boolean endWhile(){
		return tokenActual.cat == CategoriaLexica.CLENDWHILE;
	}
	
	private void funMem(){
		if(esAlloc())
			alloc();
		else if(esFree())
			free();
	}
	private void alloc(){
		sigToken();
		if(tokenActual.cat == CategoriaLexica.CLID){
			if(!existeEnTS(tokenActual.lex))
				error.escribe("Variable no existente", numInstruccion, tokenActual);
			Token t = tokenActual;
			int n = nivelIdVar(tokenActual.lex);
			int dir = tabla.get(n).getTs().dirDe(tokenActual.lex);
			Tipos tipo = dameTipo();
			MemAsig();
			sigToken();
			if(tipo != Tipos.puntero)
				error.escribeSemantico("Error de tipado, no es un puntero", dir, t);
			if(n == 0)
				genCodigo.generaAlloc(dir);
			else
				genCodigo.generaAllocBloque(dir, n);
		}
		else
			error.escribe("Token no esperado, se espera una variable", numInstruccion, tokenActual);
	}
	private void free(){
		sigToken();
		if(tokenActual.cat == CategoriaLexica.CLID){
			if(!existeEnTS(tokenActual.lex))
				error.escribe("Variable no existente", numInstruccion, tokenActual);
			Token t = tokenActual;
			int n = nivelIdVar(tokenActual.lex);
			int dir = tabla.get(n).getTs().dirDe(tokenActual.lex);
			Tipos tipo = dameTipo();
			MemAsig();
			sigToken();
			if(tipo != Tipos.puntero)
				error.escribeSemantico("Error de tipado, no es un puntero", dir, t);
			if(n == 0)
				genCodigo.generaFree(dir);
			else
				genCodigo.generaFreeBloque(dir, n);
		}
		else
			error.escribe("Caracter no esperado, se espera una variable", numInstruccion, tokenActual);
	}
	
	private void ExpCond(){
		if(tokenActual.cat == CategoriaLexica.CLPA){
			sigToken();
			Exp2();//procesamos lo que resta de expresion
			if(tokenActual.cat != CategoriaLexica.CLPC){//comprobamos que se cierra el parentesis, sino, error
				error.escribe("No cierre de parentesis",numInstruccion,tokenActual);
			}
		}
		else
			Exp2();
		sigToken();
	}
	private void acIf(){
		//AcIf ::= If Casos ElseAcIf
		sigToken(); //if consumido
		casos();
		elseAcIf();
	}
	
	private void casos(){
		//Casos ::= Caso RCasos

		caso();
		rCasos();
		
	}
	
	private void elseAcIf(){
		//ElseAcIf ::= Else Acciones EndIf
		//ElseAcIf ::= EndIf
		
		if (esElse()){
			int b=genCodigo.getEtq();
			genCodigo.ira(b);
			sigToken();
			acciones();
			genCodigo.parchea(b,genCodigo.getEtq(),true);
			if (esEndIf()){
				int c=genCodigo.getEtq();
				genCodigo.ira(c);
				genCodigo.parchea(c,genCodigo.getEtq(),true);
				sigToken();
				
			}
			else
				error.escribe("Token no esperado", numInstruccion, tokenActual);
			
		}
		else if (esEndIf()){
			int v=genCodigo.getEtq();
			genCodigo.ira(v);
			sigToken();
			genCodigo.parchea(v,genCodigo.getEtq(),true);
		}
		else if (tokenActual.cat == CategoriaLexica.CLPUNTOCOMA){
			//para ifs anidados
		}
		else
			error.escribe("Token no esperado", numInstruccion, tokenActual);
		
	}
	
	private void caso(){
		//Caso ::= Exp Then Acciones
		Token t = tokenActual;
		ExpCond();
		if(pila.desapila() != Tipos.entero)
			error.escribeSemantico("Error de tipado en la condicion del if",numInstruccion,t);
		if (esThen()){
			sigToken();
			int aux=genCodigo.getEtq();
			genCodigo.irf(aux);
			acciones();
			//genCodigo.ira();
			genCodigo.parchea(aux,genCodigo.getEtq()+1,false);
		}
		else{
			error.escribe("Token no esperado : Se espera un then", numInstruccion, tokenActual);
		}
		
	}
	
	private void rCasos(){		
		//RCasos ::= ElseIf Caso RCasos
		//RCasos ::= lambda
		
		if (esElseIf()){
			int a=genCodigo.getEtq();
			genCodigo.ira(a);
			sigToken();
			
			caso();
			genCodigo.parchea(a,genCodigo.getEtq(),true);
			rCasos();
		}
		else{
			//lambda
			
		}
	}
	
	private void acciones(){
		//Acciones ::= lambda
		//Acciones ::= SAc
		if (esEndIf() || endWhile() || esElse() || esElseIf());

		else
			sAcControl();

	}
	
	private void sAcControl(){ //si vamos a sac, tal como esta implementado, no reconoce endif
		//puede ser lambda o una accion, y tiene ke ser recursivo
		//idea:
		// SacControl ::= lambda
		if (esEndIf() || endWhile() || esElse() || esElseIf() || tokenActual.cat == CategoriaLexica.CLPUNTOCOMA){
			
		}
		else{
			Ac();
			if(tokenActual.cat == CategoriaLexica.CLPUNTOCOMA){
				sigToken();
				numInstruccion++;//sumamos una instruccion
				sAcControl();//vemos si hay mas acciones
			}
			else{
				error.escribe("Falta punto y coma",numInstruccion,tokenActual);
			}
			
		}
		
	}
	
	private void acWhile(){
		//AcWhile ::= While Exp Do Acciones EndWhile
		if (esWhile()){
			sigToken();
			int inicio=genCodigo.getEtq();
			Token t = tokenActual;
			ExpCond();
			if(pila.desapila() != Tipos.entero)
				error.escribeSemantico("Error de tipado en la condicion del while",numInstruccion,t);
			int a=genCodigo.getEtq();
			genCodigo.irf(a);
			
			if (esDo()){
				sigToken();
				acciones();
				genCodigo.ira(inicio);
				if(endWhile()){
					genCodigo.parchea(a,genCodigo.getEtq(),false);
					sigToken();
				}
				else{
					error.escribeSemantico("Token no esperado, se esperaba endwhile", numInstruccion, tokenActual);
				}				
				
			}
			
		}
		else{
			error.escribe("Token no esperado", numInstruccion, tokenActual);
		}
	}
	private int nivelIdTipo(String id){
		int i;
		for(i= nivel;i>=0;i--){
			if(tabla.get(i).getTablaTipos().existeId(id))
				return i;
		}
		return i;
	}
	private int nivelIdVar(String id){
		int i;
		for(i= nivel;i>=0;i--){
			if(tabla.get(i).getTablaVariables().existeId(id))
				return i;
		}
		return i;
	}

	private boolean existeEnTS(String id){
		for(int i = nivel;i>=0;i--)
			if(tabla.get(i).getTs().existeId(id))
				return true;
		return false;
	}
	private boolean existeTipoEnTS(String id){
		for(int i = nivel;i>=0;i--)
			if(tabla.get(i).getTs().existeIdTipo(id))
				return true;
		return false;
	}
}
